package com.zc.framework.shiro.dao;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.util.CollectionUtils;
import org.springframework.util.SerializationUtils;

import java.util.*;

/**
 * Created by zc on 2016/8/19.
 */
public class RedisShiroCache<K, V> implements Cache<K, V> {

    private static Logger logger = LogManager.getLogger(RedisShiroCache.class);

    private RedisShiroManager cache;
    private String keyPrefix = "shiro_redis_session";

    public RedisShiroCache(RedisShiroManager cache) {
        this.cache = cache;
    }

    public RedisShiroCache(RedisShiroManager cache, String keyPrefix) {
        this.cache = cache;
        this.keyPrefix = keyPrefix;
    }

    @Override
    public V get(K key) throws CacheException {
        if(key == null) {
            return null;
        } else {
            byte[] rawValue = cache.get(getByteKey(key));
            V value = (V) SerializationUtils.deserialize(rawValue);
            return value;
        }
    }

    @Override
    public V put(K key, V value) throws CacheException {
        cache.set(getByteKey(key), SerializationUtils.serialize(value));
        return value;
    }

    @Override
    public V remove(K key) throws CacheException {
        V previous = get(key);
        cache.del(getByteKey(key));
        return previous;
    }

    @Override
    public void clear() throws CacheException {
        cache.flushDB();
    }

    @Override
    public int size() {
        return new Long(cache.dbSize()).intValue();
    }

    @Override
    public Set<K> keys() {
        Set<byte[]> keys = cache.keys(this.keyPrefix + "*");
        if(CollectionUtils.isEmpty(keys)) {
            return Collections.emptySet();
        } else {
            Set<K> newKeys = new HashSet<>();
            for(byte[] key : keys){
                newKeys.add((K) key);
            }
            return newKeys;
        }
    }

    @Override
    public Collection<V> values() {
        Set<byte[]> keys = cache.keys(this.keyPrefix + "*");
        if (!CollectionUtils.isEmpty(keys)) {
            List<V> values = new ArrayList<V>(keys.size());
            for (byte[] key : keys) {
                @SuppressWarnings("unchecked")
                V value = get((K)key);
                if (value != null) {
                    values.add(value);
                }
            }
            return Collections.unmodifiableList(values);
        } else {
            return Collections.emptyList();
        }
    }

    public RedisShiroManager getCache() {
        return cache;
    }

    public void setCache(RedisShiroManager cache) {
        this.cache = cache;
    }

    private byte[] getByteKey(K key) {
        if(key instanceof String) {
            String preKey = this.keyPrefix + key;
            return preKey.getBytes();
        } else {
            return SerializationUtils.serialize(key);
        }
    }

    public String getKeyPrefix() {
        return keyPrefix;
    }

    public void setKeyPrefix(String keyPrefix) {
        this.keyPrefix = keyPrefix;
    }
}
